package com.youlu.campus.service.timetable;

import cn.hutool.core.date.LocalDateTimeUtil;
import com.google.common.collect.Lists;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.base.exception.CommonException;
import com.youlu.campus.common.utils.TimeUtils;
import com.youlu.campus.entity.timetable.UserTimetable;
import com.youlu.campus.entity.timetable.UserTimetableConfig;
import com.youlu.campus.entity.timetable.dto.UserTimetableQueryOneDTO;
import com.youlu.campus.entity.timetable.dto.UserTimetableSaveDTO;
import com.youlu.campus.entity.timetable.dto.UserTimetableQueryDTO;
import com.youlu.campus.entity.timetable.vo.UserCourseVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.text.MessageFormat;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author : [dengbo]
 * @className : UserTimetableService
 * @description : [用户课程表服务]
 * @createTime : [2023/4/21 10:01]
 */
@Slf4j
@Service
public class UserTimetableService {
    public static final List<String> WEEK_STR = Lists.newArrayList("一", "二", "三", "四", "五", "六", "天");

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private UserTimetableConfigService userTimetableConfigService;

    public UserTimetable save(UserTimetableSaveDTO req) {
        UserTimetable timetable;
        boolean isUpdate = StringUtils.isNotBlank(req.getCourseId());
        if (isUpdate) {
            Query query = new Query(Criteria.where("id").is(req.getCourseId()).and("userId").is(req.getUserId()));
            timetable = mongoTemplate.findOne(query, UserTimetable.class);
            if (timetable == null) {
                throw new BusinessException("修改课程信息不存在");
            }
        } else {
            timetable = new UserTimetable();
        }
        // 查询配置信息
        UserTimetableConfig config = userTimetableConfigService.getUserConfig(req.getActivityId(), req.getUserId());
        // 校验添加课程节数是否存在
        checkIsMerge(config, req);
        // 更新课程表周数
        LocalDate localDate = TimeUtils.toLocalDate(config.getStartDate());
        updateTimetableWeek(timetable, localDate, req.getChoiceWeeks());
        timetable.setCourseTeacher(req.getCourseTeacher());
        timetable.setCourseAddress(req.getCourseAddress());
        timetable.setCourseName(req.getCourseName());
        timetable.setUserId(req.getUserId());
        timetable.setActivityId(req.getActivityId());
        timetable.setCourseWeek(req.getCourseWeek());
        timetable.setCourseNum(req.getCourseNum());
        int colorTyp = getHashColorNum(req.getCourseName());
        timetable.setColorType(String.valueOf(colorTyp));
        timetable.setSemester(config.getSemester());
        timetable.setGrade(config.getGrade());
        mongoTemplate.save(timetable);
        return timetable;
    }

    public boolean checkIsMerge(UserTimetableConfig config, UserTimetableSaveDTO req) {
        boolean isUpdate = StringUtils.isNotBlank(req.getCourseId());
        // 查询课程当前的某周某星期是否添加
        List<UserTimetable> list = queryUserTimetableByWeek(config, req.getCourseWeek());
        if (CollectionUtils.isNotEmpty(list)) {
            for (UserTimetable tb : list) {
                if (isUpdate && tb.getId().equals(req.getCourseId())) {
                    continue;
                }
                if (!tb.getCourseWeek().equals(req.getCourseWeek())) {
                    continue;
                }
                tb.getChoiceWeeks().retainAll(req.getChoiceWeeks());
                if (CollectionUtils.isEmpty(tb.getChoiceWeeks())) {
                    continue;
                }
                tb.getCourseNum().retainAll(req.getCourseNum());
                if (CollectionUtils.isEmpty(tb.getCourseNum())) {
                    continue;
                }

                String msg = MessageFormat.format("检测到同时间有课程，【{0}】星期{1}包含节数{2}，请先调整", tb.getCourseName(), WEEK_STR.get(tb.getCourseWeek() - 1), tb.getCourseNum());
                throw new CommonException(60000, msg);
            }
        }
        return true;
    }

    public void updateTimetableWeek(UserTimetable timetable, LocalDate startDate, List<Integer> choiceWeeks) {
        List<Integer> realWeeks = new ArrayList<>();
        List<String> yearRealWeek = new ArrayList<>();
        Set<String> containsYear = new HashSet<>();
        DayOfWeek dayOfWeek = startDate.getDayOfWeek();
        int dayWeek = dayOfWeek.getValue();
        choiceWeeks.forEach(item -> {
            LocalDate newDate = startDate.plusWeeks(item - 1);
            int week = getWeek(newDate);
            realWeeks.add(week);
            yearRealWeek.add(newDate.getYear() + "-" + week);
            containsYear.add(String.valueOf(newDate.getYear()));
            log.info("course:{} date:{} week:{}", timetable.getCourseName(), newDate, week);
        });
        Integer maxWeek = choiceWeeks.stream().mapToInt(Integer::intValue).max().orElse(1);
        // 结束日期,刚好凑成整周
        LocalDate endDate = startDate.plusWeeks(maxWeek - 1).plusDays(7 - dayWeek);
        timetable.setStartDate(TimeUtils.localDateToDate(startDate));
        timetable.setEndDate(TimeUtils.localDateToDate(endDate));
        timetable.setRealWeeks(realWeeks);
        timetable.setYearRealWeeks(yearRealWeek);
        timetable.setChoiceWeeks(choiceWeeks);
        timetable.setYear(String.valueOf(startDate.getYear()));
        timetable.setYears(containsYear);
    }

    public void updateUserAllCourseByConfig(UserTimetableConfig config) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(config.getUserId()));
        query.addCriteria(Criteria.where("activityId").is(config.getActivityId()));
        List<UserTimetable> list = mongoTemplate.find(query, UserTimetable.class);
//        LocalDate startDate = config.getStartDate();
        LocalDate startDate = TimeUtils.toLocalDate(config.getStartDate());
        list.forEach(timetable -> {
            updateTimetableWeek(timetable, startDate, timetable.getChoiceWeeks());
            timetable.setColorType(String.valueOf(getHashColorNum(timetable.getCourseName())));
            if (timetable.getGrade() == null) {
                timetable.setGrade(config.getGrade());
            }
            mongoTemplate.save(timetable);
        });
    }


    /**
     * week 是1-7  1表示周一 7表示周天
     *
     * @param week
     */
    public static int getColorNum(int week, int courseNum) {
        int total = week + courseNum - 1;
        return total > 10 ? total % 10 : total;
    }

    public static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public static int getHashColorNum(String str) {
        return Math.abs(hash(str) + str.hashCode()) % 10 + 1;
    }

    /**
     * 字符串取hash % 10
     */
    public static int getNewHashColorNum(String str) {
        return Math.abs(str.hashCode()) % 10 + 1;
    }

    public static boolean isMerge(List<Integer> list, List<Integer> coll) {
        IntSummaryStatistics statistics = list.stream().mapToInt(Integer::intValue).summaryStatistics();
        int min = statistics.getMin();
        int max = statistics.getMax();
        log.info("min:{} max:{}", min, max);
        return (coll.contains(min - 1) || coll.contains(max + 1));
    }

    public Object queryTimetableByDate(UserTimetableQueryDTO queryDTO) {
        List<UserCourseVo> list = queryUserTimetableByDate(queryDTO);
        Map<Integer, List<UserCourseVo>> result = list.stream().collect(Collectors.groupingBy(UserCourseVo::getCourseWeek));
        return result;
    }

    public UserCourseVo queryTimetableById(UserTimetableQueryOneDTO queryDTO) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(queryDTO.getUserId()));
        query.addCriteria(Criteria.where("id").is(queryDTO.getCourseId()));
        UserTimetable userTimetable = mongoTemplate.findOne(query, UserTimetable.class);
        if (userTimetable == null) {
            log.error(":>>> 用户的课程表不存在:{},{}",queryDTO.getUserId(),queryDTO.getCourseId());
            throw new BusinessException("课程表信息不存在");
        }
        UserCourseVo vo = new UserCourseVo();

        BeanUtils.copyProperties(userTimetable, vo);
        return vo;
    }

    public boolean deleteTimetableById(UserTimetableQueryOneDTO queryDTO) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(queryDTO.getUserId()));
        query.addCriteria(Criteria.where("id").is(queryDTO.getCourseId()));
        UserTimetable userTimetable = mongoTemplate.findOne(query, UserTimetable.class);
        if (userTimetable == null) {
            throw new BusinessException("课程表信息不存在");
        }
        mongoTemplate.remove(query, UserTimetable.class);
        return true;
    }


    public List<UserTimetable> queryByWeekYear(UserTimetableQueryDTO queryDTO) {
        String grade = "2022-2023";
        String semester = "1";
        String dateStr = queryDTO.getDate();
        LocalDate date = LocalDateTimeUtil.parseDate(dateStr);
        int week = getWeek(date);
        String year = String.valueOf(date.getYear());
        String yearWeek = year + "-" + week;
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(queryDTO.getUserId()));
        query.addCriteria(Criteria.where("activityId").is(queryDTO.getActivityId()));
        query.addCriteria(Criteria.where("grade").is(grade));
        query.addCriteria(Criteria.where("semester").is(semester));
        query.addCriteria(Criteria.where("yearRealWeeks").elemMatch(Criteria.where(yearWeek)));
        List<UserTimetable> list = mongoTemplate.find(query, UserTimetable.class);
        return list;
    }

    public List<UserTimetable> queryUserTimetableByWeek(UserTimetableConfig config, int courseWeek) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(config.getUserId()));
        query.addCriteria(Criteria.where("activityId").is(config.getActivityId()));
        query.addCriteria(Criteria.where("grade").is(config.getGrade()));
        query.addCriteria(Criteria.where("semester").is(config.getSemester()));
        query.addCriteria(Criteria.where("courseWeek").is(courseWeek));
        List<UserTimetable> list = mongoTemplate.find(query, UserTimetable.class);
        return list;
    }

    public List<UserCourseVo> queryUserTimetableByDate(UserTimetableQueryDTO queryDTO) {
        String dateStr = queryDTO.getDate();
        LocalDate date = LocalDateTimeUtil.parseDate(dateStr);
        return queryUserTimetableByDate(queryDTO.getUserId(), queryDTO.getActivityId(), date, queryDTO.getType());
    }

    public List<UserCourseVo> queryUserTimetableByDate(String userId, String activityId, LocalDate date, Integer type) {
        String grade = "2022-2023";
        String semester = "1";
        boolean isDay = type == null || type == 0;
        int realWeek = getWeek(date);
        String year = String.valueOf(date.getYear());
        String yearWeek = year + "-" + realWeek;
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        int courseWeek = dayOfWeek.getValue();
        log.info("input date current week-year:{},day of week:{}", yearWeek, dayOfWeek);
        Query query = new Query();
        query.addCriteria(Criteria.where("userId").is(userId));
        query.addCriteria(Criteria.where("activityId").is(activityId));
        query.addCriteria(Criteria.where("grade").is(grade));
        query.addCriteria(Criteria.where("semester").is(semester));
        List<UserTimetable> list = mongoTemplate.find(query, UserTimetable.class);
        // 课程列表
        return list.stream().filter(item -> (!isDay || courseWeek == item.getCourseWeek()) && item.getRealWeeks().contains(realWeek))
                .map(item -> {
                    UserCourseVo vo = new UserCourseVo();
                    BeanUtils.copyProperties(item, vo);
                    return vo;
                })
                .collect(Collectors.toList());
    }

    public Set<String> getLocalCourseNameList(String userId, String activityId) {
        List<UserCourseVo> list = queryUserTimetableByDate(userId, activityId, LocalDate.now(), 0);
        return list.stream().map(UserCourseVo::getCourseName).collect(Collectors.toSet());
    }


    public static int getWeek(LocalDate localDate) {
        // 第一个参数：新年第一天 从周几算一周的第一天，不能为空
        // 第二个参数：第一周的最小天数，从1到7
        WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY, 1);
        //WeekFields weekFields = WeekFields.ISO;
        //int weekOfYear = localDate.get(weekFields.weekBasedYear());
        //log.error("current week of year:{},year1:{}", weekOfYear, localDate.getYear());
        int week = localDate.get(weekFields.weekOfWeekBasedYear());
        log.error("current week:{}", week);
        return week;
    }
}
